/*
	Azid Engine is 3D Game Engine developed along side with Galactic Siege (see Google Code) 
	Copyright (C) 2010  Jorgen Ader

	This file is part of AzidEngine.

	AzidEngine is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	AzidEngine is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with AzidEngine.  If not, see <http://www.gnu.org/licenses/>. 
*/

#include "../includes/AzidEngine.hpp"

using namespace AzidEngine;

AzidGame* AzidGame::_this = 0;

void AzidGame::initEngine(Ogre::String wndTitle, int height, int width, short screenmode)
{
	SettingsMgr = new SettingsManager("settings.cfg");
	Ogre::LogManager* logMgr = new Ogre::LogManager();
	LogMgr = logMgr->createLog("LogFile.log", true, true, false);
#ifdef _DEBUG
	LogMgr->setDebugOutputEnabled(true);
	LogMgr->logMessage("__USING_DEBUG_BUILD__");
#else
	LogMgr->logMessage("__USING_RELEASE_BUILD__");
	LogMgr->setDebugOutputEnabled(false);
#endif
	LogMgr->logMessage("Using AzidEngine Version:"+Ogre::String(MAJOR_VERSION)+"."+
			Ogre::String(MINOR_VERSION)+"."+Ogre::String(MINOR_SUB_VERSION)+" Codenamed:"+Ogre::String(CODENAME));
	Root = new Ogre::Root();
	// OGRE Plugins to load
#ifdef _DEBUG
#	if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 
#	endif
	Root->loadPlugin("RenderSystem_GL_d");
	Root->loadPlugin("Plugin_OctreeSceneManager_d");
	Root->loadPlugin("Plugin_BSPSceneManager_d");
	Root->loadPlugin("Plugin_CgProgramManager_d");
	Root->loadPlugin("Plugin_PCZSceneManager_d");
	Root->loadPlugin("Plugin_ParticleFX_d");
#else
#	if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 
#	endif
	Root->loadPlugin("RenderSystem_GL");
	Root->loadPlugin("Plugin_OctreeSceneManager");
	Root->loadPlugin("Plugin_BSPSceneManager");
	Root->loadPlugin("Plugin_CgProgramManager");
	Root->loadPlugin("Plugin_PCZSceneManager");
	Root->loadPlugin("Plugin_ParticleFX");
#endif

	bool bSettings = SettingsMgr->Read();

	/* Setting Graphics options */
	Ogre::String str1, str2, str3;
	if(!bSettings)
	{
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
		str1 = "Direct3D9 Rendering Subsystem";
		SettingsMgr->setValue(SettingsManager::RENDERING_SYSTEM, str1);
#else
		str1 = "OpenGL3 Rendering Subsystem";
		SettingsMgr->setValue(SettingsManager::RENDERING_SYSTEM, str1);
#endif
	}
	else
	{
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
		str1 = SettingsMgr->getValue(SettingsManager::RENDERING_SYSTEM);
		if(str1 == "Direct3D9" || str1 == "Direct3D10")
			str1 += " Rendering Subsystem";
		else
			str1 = "Direct3D9 Rendering Subsystem";
#else
		str1 = SettingsMgr->getValue(SettingsManager::RENDERING_SYSTEM);
		if(str1 == "OpenGL")
			str1 += " Rendering Subsystem";
		else
			str1 = "OpenGL Rendering Subsystem";
#endif
	}
		
	/* Values: Direct3D9, Direct3D10, Direct3D11, OpenGL */
	/* Default: Direct3D9(Windows) & OpenGL(Linux) */
	LogMgr->logMessage("Using " + str1 + "...");
	Ogre::RenderSystem *rs = Root->getRenderSystemByName(str1);
	Root->setRenderSystem(rs);

	if(!bSettings)
	{
		str1 = "No";
		SettingsMgr->setValue(SettingsManager::FULLSCREEN, str1);
		LogMgr->logMessage("Fullscreen: " + str1);
	}
	else
	{
		str1 = SettingsMgr->getValue(SettingsManager::FULLSCREEN);
		if(str1 != "Yes" && str1 != "No")
		{
			str1 = "No";
			SettingsMgr->setValue(SettingsManager::FULLSCREEN, str1);
		}
	}
	rs->setConfigOption("Full Screen", str1);
	/* Values: Yes, No. */
	/* Default: Yes. */

	if(!bSettings)
	{
		SettingsMgr->setValue(SettingsManager::SCREEN_WIDTH, "1024");
		SettingsMgr->setValue(SettingsManager::SCREEN_HEIGHT, "768");
		SettingsMgr->setValue(SettingsManager::COLOR_DEPTH, "32");
		str1 = "1024 x 768 @ 32-bit colour";
	}
	else
	{
		str2 = SettingsMgr->getValue(SettingsManager::SCREEN_WIDTH) + " x " + SettingsMgr->getValue(SettingsManager::SCREEN_HEIGHT);
		str3 = SettingsMgr->getValue(SettingsManager::COLOR_DEPTH);

		if(str2 != "640 x 480" && str2 != "720 x 480" && str2 != "720 x 576" && str2 != "800 x 600" && str2 != "1024 x 768" &&
			str2 != "1152 x 864" && str2 != "1280 x 720" && str2 != "1280 x 768" && str2 != "1280 x 800" && str2 != "1440 x 900")
		{
			SettingsMgr->setValue(SettingsManager::SCREEN_WIDTH, "1024");
			SettingsMgr->setValue(SettingsManager::SCREEN_HEIGHT, "768");
		}

		if(str3 != "32" && str3 != "16")
		{
			str3 = "32";
			SettingsMgr->setValue(SettingsManager::COLOR_DEPTH, str3);
		}

		str1 = str2 + " @ " + str3 + "-bit colour";
	}
	rs->setConfigOption("Video Mode", str1);
	/*
	---------------
	Video Mode
	---------------
	640 x 480 @ 16-bit colour
	720 x 480 @ 16-bit colour
	720 x 576 @ 16-bit colour
	800 x 600 @ 16-bit colour
	1024 x 768 @ 16-bit colour 
	1152 x 864 @ 16-bit colour
	1280 x 720 @ 16-bit colour
	1280 x 768 @ 16-bit colour
	1280 x 800 @ 16-bit colour
	1440 x 900 @ 16-bit colour
	640 x 480 @ 32-bit colour
	720 x 480 @ 32-bit colour
	720 x 576 @ 32-bit colour
	800 x 600 @ 32-bit colour  -- default
	1024 x 768 @ 32-bit colour -- default
	1152 x 864 @ 32-bit colour
	1280 x 720 @ 32-bit colour
	1280 x 768 @ 32-bit colour
	1280 x 800 @ 32-bit colour
	1440 x 900 @ 32-bit colour
	**/
	str2.clear();
	str3.clear();

	if(!bSettings)
	{
		str1 = "None";
		SettingsMgr->setValue(SettingsManager::ANTI_ALIASING, str1);
	}
	else
	{
		str1 = SettingsMgr->getValue(SettingsManager::ANTI_ALIASING);
		if(str1 != "None" && str1 != "NonMaskable 1" && str1 != "NonMaskable 2" && str1 != "NonMaskable 3" &&
			str1 != "NonMaskable 4" && str1 != "NonMaskable 5" && str1 != "NonMaskable 6" && str1 != "NonMaskable 7" &&
			str1 != "NonMaskable 8" && str1 != "Level 2" && str1 != "Level 4" && str1 != "Level 8")
			str1 = "None";
	}
	rs->setConfigOption("Anti aliasing", str1);
	/* Values: None, NonMaskable 1 - 8, Level 2, Level 4, Level 8 */
	/* Default: None. */

	if(!bSettings)
	{
		str1 = "No";
		SettingsMgr->setValue(SettingsManager::VSYNC, str1);
	}
	else
	{
		str1 = SettingsMgr->getValue(SettingsManager::VSYNC);
		if(str1 != "Yes" && str1 != "No")
			str1 = "No";
	}
	rs->setConfigOption("VSync", str1);
	/* Values: Yes, No. */
	/* Default: No. */

	if(!bSettings)
	{
		str1 = "No";
		SettingsMgr->setValue(SettingsManager::NVPREF_HUD, str1);
	}

	else
	{
		str1 = SettingsMgr->getValue(SettingsManager::NVPREF_HUD);
		if(str1 != "Yes" && str1 != "No")
			str1 = "No";
	}
	rs->setConfigOption("Allow NVPerfHUD", str1);
	/* Values: Yes, No. */
	/* Default: No. */

	if(!bSettings)
	{
		str1 = "No";
		SettingsMgr->setValue(SettingsManager::sRGB_CONVERSION, str1);
	}
	else
	{
		str1 = SettingsMgr->getValue(SettingsManager::sRGB_CONVERSION);
		if(str1 != "Yes" && str1 != "No")
			str1 = "No";
	}
	rs->setConfigOption("sRGB Gamma Conversion", str1);
	/* Values: Yes, No. */
	/* Default: No. */

	if(!bSettings)
	{
		str1 = "Fastest";
		SettingsMgr->setValue(SettingsManager::FLOATING_POINT, str1);
	}
	else
	{
		str1 = SettingsMgr->getValue(SettingsManager::FLOATING_POINT);
		if(str1 != "Fastest" && str1 != "Consisten")
			str1 = "Fastest";
	}
	rs->setConfigOption("Floating-point mode", str1);
	/* Values:  Fastest, Consisten. */
	/* Default: Fastest. */

	str1.clear();
	SettingsMgr->Write();

	RenderWnd = Root->initialise(true, wndTitle);

	Viewport = RenderWnd->addViewport(0);
	Viewport->setBackgroundColour(Ogre::ColourValue::Black);
	Viewport->setCamera(0);

	unsigned long hwnd = 0;
	OIS::ParamList paramList;

	paramList.insert(OIS::ParamList::value_type("WINDOW", Ogre::StringConverter::toString(hwnd)));

	InputMgr = OIS::InputManager::createInputSystem(paramList);
	KeyboardHandler = static_cast<OIS::Keyboard*>(InputMgr->createInputObject(OIS::OISKeyboard, true));
	MouseHandler = static_cast<OIS::Mouse*>(InputMgr->createInputObject(OIS::OISMouse, true));

	MouseHandler->getMouseState().height = RenderWnd->getHeight();
	MouseHandler->getMouseState().width = RenderWnd->getWidth();

	KeyboardHandler->setEventCallback(this);
	MouseHandler->setEventCallback(this);
	
	Ogre::ResourceGroupManager *RGM = Ogre::ResourceGroupManager::getSingletonPtr();
	Ogre::String secName, typeName, archName;
	Ogre::ConfigFile cf;
	cf.load((SettingsMgr->getValue(SettingsManager::RESOURCEFILE)!="")?
			"../Data/"+SettingsMgr->getValue(SettingsManager::RESOURCEFILE):"../Data/resource.cfg");
	Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
	while(seci.hasMoreElements())
	{
		secName = seci.peekNextKey();
		Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
		Ogre::ConfigFile::SettingsMultiMap::iterator i;
		for(i = settings->begin(); i != settings->end(); ++i)
		{
			typeName = i->first;
			archName = i->second;
			RGM->addResourceLocation(archName, typeName, secName);
		}
	}
	
	Ogre::TextureManager::getSingletonPtr()->setDefaultNumMipmaps(5);
	RGM->initialiseAllResourceGroups();

	Timer = new Ogre::Timer();
	Timer->reset();

	GuiRenderer = CEGUI::OgreRenderer::bootstrapSystem();

	RenderWnd->setActive(true);
	
	mSensFactor.renderDistanceFactor = (SettingsMgr->getValue(SettingsManager::LOV) == "")?
			5.0:Ogre::StringConverter::parseReal(SettingsMgr->getValue(SettingsManager::LOV));
	
	StateMgr = new StateManager;
	ModMgr = new ModManager;
	SoundMgr = new OgreOggSound::OgreOggSoundManager;
	Skin = SettingsMgr->getValue(SettingsManager::UI_SKIN)!=""?SettingsMgr->getValue(SettingsManager::UI_SKIN):"AzidSkin";
	// TODO add ingame console, easier debugging
}

void AzidGame::startGame()
{
	double frameTime = 1;
	double start = 0;

	// the main render loop
	while(!mQuit)
	{
		if(RenderWnd->isClosed())
			mQuit = true;
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
		Ogre::WindowEventUtilities::messagePump();
#endif
		start = Timer->getMillisecondsCPU();
		KeyboardHandler->capture();
		MouseHandler->capture();
		/* Begin of Updates */
		if(CEGUI::System::getSingletonPtr())
			CEGUI::System::getSingletonPtr()->injectTimePulse(Ogre::Real(frameTime / 1000)); // Because CEGUI want's TimeElapsed in seconds
		StateMgr->onCurrentUpdate();
		/* End of Updates */
		Root->renderOneFrame();
		frameTime = Timer->getMillisecondsCPU() - start;
	}
	LogMgr->logMessage("Render Loop ended");
	LogMgr->logMessage("AzidEngine Shutdown...");
}

bool AzidGame::keyPressed(const OIS::KeyEvent &evt)
{
	if(mConsoleActive)
	{
		CEGUI::System::getSingletonPtr()->injectKeyDown(evt.key);
		CEGUI::System::getSingletonPtr()->injectChar(evt.text);
	}
	return true;
}

bool AzidGame::keyReleased(const OIS::KeyEvent &evt)
{
	return true;
}

bool AzidGame::mouseMoved(const OIS::MouseEvent &evt)
{
	return true;
}

bool AzidGame::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
	return true;
}

bool AzidGame::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
	return true;
}

void AzidGame::actionQuit()
{
	mQuit = true;
	StateMgr->onDestroyCurrentState();
}

void AzidGame::makeScreenShot()
{
	std::ostringstream oss;
	time_t mTime;
	struct tm * tInfo;
	char sTime[80];

	time( &mTime );
	tInfo = localtime( &mTime );
	strftime( sTime, 80, "%d-%m-%Y_%H:%M:%S", tInfo );
	oss << "screenshot_" << sTime << ".png";
	AzidGame::RenderWnd->writeContentsToFile(oss.str());
}
